﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Castle.ActiveRecord;
using Castle.MonoRail.ActiveRecordSupport;
using Castle.MonoRail.ExtensionMethods;
using Castle.MonoRail.ExtensionMethods.Helpers;

namespace Castle.MonoRail.ActiveRecord.ExtensionMethods {
	public class ARUtils: Utils {
		public virtual string GetARDataBindPrefix(ParameterInfo p) {
			var db = p.GetCustomAttributes<ARDataBindAttribute>();
			if (db.Length == 0)
				return null;
			return db[0].Prefix;
		}

		public virtual string GetARFetch(ParameterInfo p) {
			var db = p.GetCustomAttributes<ARFetchAttribute>();
			if (db.Length == 0)
				return null;
			return db[0].RequestParameterName ?? p.Name;
		}

		public virtual PropertyInfo GetPrimaryKey(Type t) {
			return t.GetProperties().FirstOrDefault(p => p.HasCustomAttribute<PrimaryKeyAttribute>());
		}

		public override IEnumerable<DictionaryEntry> GetValue(MethodCallExpression body, Expression exp, ParameterInfo param) {
			var prefix = GetARDataBindPrefix(param);
			if (prefix != null)
				return GetValueDataBind(exp, prefix);
			var fetch = GetARFetch(param);
			if (fetch != null)
				return GetValueFetch(exp, fetch);
			return base.GetValue(body, exp, param);
		}

		public virtual IEnumerable<DictionaryEntry> GetValueFetch(Expression exp, string fetch) {
			var o = GetExpressionValue(exp);
            if (o == null) {
                yield return new DictionaryEntry(fetch, null);
                yield break;
            }
			var pk = GetPrimaryKey(o.GetType());
			if (pk == null)
				throw new ArgumentException(string.Format("No primary key found for type '{0}'", o.GetType()));
			yield return new DictionaryEntry(fetch, pk.GetValue(o, null));
		}
	}
}